﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.Activities
    <SRDescription("SequenceActivityDescription"), Designer(GetType(SequenceDesigner), GetType(IDesigner)), ToolboxBitmap(GetType(SequenceActivity), "Resources.Sequence.png"), SRCategory("Standard"), ToolboxItem(GetType(ActivityToolboxItem))> _
    Public Class SequenceActivity
        Inherits CompositeActivity
        Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs)
        ' Methods
        Public Sub New()
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
        End Sub

        Protected Overrides Function Cancel(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Dim i As Integer = (MyBase.EnabledActivities.Count - 1)
            Do While (i >= 0)
                Dim activity As Activity = MyBase.EnabledActivities.Item(i)
                If (activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                    executionContext.CancelActivity(activity)
                    Return ActivityExecutionStatus.Canceling
                End If
                If ((activity.ExecutionStatus = ActivityExecutionStatus.Canceling) OrElse (activity.ExecutionStatus = ActivityExecutionStatus.Faulting)) Then
                    Return ActivityExecutionStatus.Canceling
                End If
                If (activity.ExecutionStatus = ActivityExecutionStatus.Closed) Then
                    MyBase.RemoveProperty(SequenceActivity.ActiveChildQualifiedNameProperty)
                    Return ActivityExecutionStatus.Closed
                End If
                i -= 1
            Loop
            Return ActivityExecutionStatus.Closed
        End Function

        Protected Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (MyBase.EnabledActivities.Count = 0) Then
                Me.OnSequenceComplete(executionContext)
                Return ActivityExecutionStatus.Closed
            End If
            MyBase.EnabledActivities.Item(0).RegisterForStatusChange(Activity.ClosedEvent, Me)
            executionContext.ExecuteActivity(MyBase.EnabledActivities.Item(0))
            MyBase.SetValue(SequenceActivity.ActiveChildQualifiedNameProperty, MyBase.EnabledActivities.Item(0).QualifiedName)
            Return ActivityExecutionStatus.Executing
        End Function

        Protected Overrides Function HandleFault(ByVal executionContext As ActivityExecutionContext, ByVal exception As Exception) As ActivityExecutionStatus
            MyBase.SetValue(SequenceActivity.SequenceFaultingProperty, True)
            Dim status As ActivityExecutionStatus = MyBase.HandleFault(executionContext, exception)
            If (status = ActivityExecutionStatus.Closed) Then
                MyBase.RemoveProperty(SequenceActivity.SequenceFaultingProperty)
            End If
            Return status
        End Function

        Protected Overrides Sub OnActivityChangeRemove(ByVal executionContext As ActivityExecutionContext, ByVal removedActivity As Activity)
            Dim str As String = [TryCast](MyBase.GetValue(SequenceActivity.ActiveChildQualifiedNameProperty),String)
            If removedActivity.QualifiedName.Equals(str) Then
                Me.activeChildRemovedInDynamicUpdate = True
            End If
            MyBase.OnActivityChangeRemove(executionContext, removedActivity)
        End Sub

        Protected Overridable Sub OnSequenceComplete(ByVal executionContext As ActivityExecutionContext)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            MyBase.RemoveProperty(SequenceActivity.ActiveChildQualifiedNameProperty)
        End Sub

        Protected Overrides Sub OnWorkflowChangesCompleted(ByVal executionContext As ActivityExecutionContext)
            If (TypeOf MyBase.GetValue(SequenceActivity.ActiveChildQualifiedNameProperty) Is String AndAlso Me.activeChildRemovedInDynamicUpdate) Then
                If ((MyBase.ExecutionStatus = ActivityExecutionStatus.Canceling) OrElse ((MyBase.ExecutionStatus = ActivityExecutionStatus.Faulting) AndAlso CBool(MyBase.GetValue(SequenceActivity.SequenceFaultingProperty)))) Then
                    If (MyBase.ExecutionStatus = ActivityExecutionStatus.Faulting) Then
                        MyBase.RemoveProperty(SequenceActivity.SequenceFaultingProperty)
                    End If
                    MyBase.RemoveProperty(SequenceActivity.ActiveChildQualifiedNameProperty)
                    executionContext.CloseActivity
                ElseIf ((MyBase.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso Not Me.TryScheduleNextChild(executionContext)) Then
                    Me.OnSequenceComplete(executionContext)
                    executionContext.CloseActivity
                End If
            End If
            Me.activeChildRemovedInDynamicUpdate = False
            MyBase.OnWorkflowChangesCompleted(executionContext)
        End Sub

        Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.ActivityExecutionStatusChangedEventArgs>.OnEvent(ByVal sender As Object, ByVal e As ActivityExecutionStatusChangedEventArgs) Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs).OnEvent
            If (sender Is Nothing) Then
                Throw New ArgumentNullException("sender")
            End If
            If (e Is Nothing) Then
                Throw New ArgumentNullException("e")
            End If
            Dim executionContext As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (executionContext Is Nothing) Then
                Throw New ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender")
            End If
            e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
            Dim activity As SequenceActivity = [TryCast](executionContext.Activity,SequenceActivity)
            If (activity Is Nothing) Then
                Throw New ArgumentException("sender")
            End If
            If ((activity.ExecutionStatus = ActivityExecutionStatus.Canceling) OrElse ((activity.ExecutionStatus = ActivityExecutionStatus.Faulting) AndAlso CBool(MyBase.GetValue(SequenceActivity.SequenceFaultingProperty)))) Then
                If (activity.ExecutionStatus = ActivityExecutionStatus.Faulting) Then
                    MyBase.RemoveProperty(SequenceActivity.SequenceFaultingProperty)
                End If
                MyBase.RemoveProperty(SequenceActivity.ActiveChildQualifiedNameProperty)
                executionContext.CloseActivity
            ElseIf ((activity.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso Not Me.TryScheduleNextChild(executionContext)) Then
                Me.OnSequenceComplete(executionContext)
                executionContext.CloseActivity
            End If
        End Sub

        Private Function TryScheduleNextChild(ByVal executionContext As ActivityExecutionContext) As Boolean
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Dim enabledActivities As IList([Of] Activity) = MyBase.EnabledActivities
            If (enabledActivities.Count = 0) Then
                Return False
            End If
            Dim num As Integer = 0
            Dim i As Integer = (enabledActivities.Count - 1)
            Do While (i >= 0)
                If (enabledActivities.Item(i).ExecutionStatus = ActivityExecutionStatus.Closed) Then
                    If (i = (enabledActivities.Count - 1)) Then
                        Return False
                    End If
                    num = (i + 1)
                    Exit Do
                End If
                i -= 1
            Loop
            enabledActivities.Item(num).RegisterForStatusChange(Activity.ClosedEvent, Me)
            executionContext.ExecuteActivity(enabledActivities.Item(num))
            MyBase.SetValue(SequenceActivity.ActiveChildQualifiedNameProperty, enabledActivities.Item(num).QualifiedName)
            Return True
        End Function


        ' Fields
        Private Shared ReadOnly ActiveChildQualifiedNameProperty As DependencyProperty = DependencyProperty.Register("ActiveChildQualifiedName", GetType(String), GetType(SequenceActivity))
        <NonSerialized> _
        Private activeChildRemovedInDynamicUpdate As Boolean
        Private Shared ReadOnly SequenceFaultingProperty As DependencyProperty = DependencyProperty.Register("SequenceFaulting", GetType(Boolean), GetType(SequenceActivity))
    End Class
End Namespace

